package helper.scxml.scxml2.zone.t2

import helper.base.A2LHM
import helper.base.A2Tuple
import helper.base.BaseTypeHelper.ListExpand.toArrayList
import helper.base.CloneHelper.Expand.StringList.cloneList
import helper.base.LHMHelper.A2LHMExpand.add
import helper.base.StateHelper.State
import helper.base.StringHelper.removeEmptyLine
import helper.base.TupleHelper.GetTuple.getA2Tuple
import helper.base.ZoneConstraintHelper.Constraint
import helper.base.debug.DebugHelper.DebuggerList
import helper.base.debug.DebugHelper.DebuggerList.Companion.makeDebuggerList
import helper.base.math.MathHelper.Sign.Obj.aDivisionSign
import helper.base.math.MathHelper.Sign.Obj.aMultipleSign
import helper.base.math.MathHelper.ToMathLStringHelper.toPreSymbolString
import helper.base.math.MathHelper.modifiedMinus
import helper.base.math.TreeHelper.T1HierarchyStructure
import helper.base.math.TreeHelper.T2HierarchyStructure
import helper.base.string.ToStrHelper.IToStr
import helper.base.string.ToStrHelper.TabStringUnit.Companion.makeTabStringUnit
import helper.scxml.scxml2.zone.t2.ConfigHelper.Config
import helper.scxml.strategy.StrategyHelper.TimedEventHelper.TimedEvent
import helper.scxml.strategy.StrategyHelper.TimedEventHelper.TimedEventMinTimePQ.Companion.makeTimedEventMinTimePQ

typealias TimedEventConfigTuple = A2Tuple<TimedEvent?, Config>
typealias TimedEventListConfigTuple = A2Tuple<ArrayList<TimedEvent>, Config>

object ConfigHelper {
    class Config(
        val states: ArrayList<String>,
        // 约束条件列表，这些条件之间是“与”（AND）关系
        var varConstraintLHM: A2LHM<String, Constraint>,
    ) : State(), IToStr {
        // 用于管理时间事件选择的最小时间优先队列
        var heap = makeTimedEventMinTimePQ()

        // 伴生对象提供了几种静态方法来创建 Config 实例
        companion object {
            fun makeConfig(
                states: ArrayList<String>,
                varConstraintLHM: A2LHM<String, Constraint>,
            ): Config {
                return Config(states, varConstraintLHM)
            }

            fun makeConfig(
                states: ArrayList<String>,
                zcs: ArrayList<Constraint>,
            ): Config {
                val lhm = A2LHM<String, Constraint>()
                zcs.forEach {
                    lhm.add(it.getVariablesString(), it)
                }
                return Config(states, lhm)
            }

            // 构造方法重载，接受位置列表和可变数量的约束
            fun makeConfig(
                states: ArrayList<String>,
                vararg zcs: Constraint,
            ): Config {
                return makeConfig(states, arrayListOf(*zcs))
            }

            // 构造方法重载，接受单个位置和可变数量的约束
            fun makeConfig(
                states: String,
                vararg zcs: Constraint,
            ): Config {
                return makeConfig(arrayListOf(states), arrayListOf(*zcs))
            }
        }

        // ConfigBoolFun 对象定义了几个与 Config 相关的布尔函数
        object ConfigBoolFun {
            val aConfigTrueFun = { _: Config -> true } // 总是返回 true 的函数
            val aHeapNotContainsEventFun: (Config, String) -> Boolean = { zs, event ->
                zs.heap.notContainsEvent(event) // 判断给定事件是否不在事件优先队列中
            }
            val aHeapNotContainsEventsFun: (Config, List<String>) -> Boolean = { zs, events ->
                events.all {
                    zs.heap.notContainsEvent(it) // 判断给定事件列表中的所有事件是否都不在事件优先队列中
                }
            }
        }

        // 获取与当前 Config 关联的所有祖先位置的 T1ParentChildRelations 实例
        fun getT1ParentChildRelations(aT2ParentChildRelations: T2HierarchyStructure<String>): T1HierarchyStructure<String> {
            return aT2ParentChildRelations.findAllAncestors(states)
        }

        // 创建 Config 实例的深克隆
        fun deepClone(): Config {
            val lhm = A2LHM<String, Constraint>()
            varConstraintLHM.forEach { (k: String, v: Constraint) ->
                lhm.add(k, v.clone())
            }

            return makeConfig(
                states.cloneList(),
                lhm,
            ).also {
                it.name = name
                it.heap = heap.deepClone()
            }
        }

        // 克隆当前 Config 实例
        override fun clone() = this.deepClone()

        // 获取位置列表中的第一个位置的字符串表示
        fun getState0String(): String {
            require(states.size > 0)
            return states.first()
        }

        // 使用自定义函数将 Config 实例转换为字符串表示（不带缩进）
        fun toT0Str(
            aStatesStringFun: ArrayList<String>.() -> String = {
                this.joinToString(separator = ",")
            },
            aCsStringFun: ArrayList<Constraint>.() -> String = {
                this.joinToString(",") {
                    it.toStr0()
                }
            },
        ): String {
            val stringBuilder = StringBuilder()

            stringBuilder.append("(")
            stringBuilder.append("(")
            stringBuilder.append(
                this.states.aStatesStringFun()
            )
            stringBuilder.append("),(")
            stringBuilder.append(
                this.varConstraintLHM.values.toList().toArrayList().aCsStringFun(),
            )
            stringBuilder.append("),")
            stringBuilder.append(this.heap.getString())
            stringBuilder.append(")")

            return stringBuilder.toString()
        }

        // 使用自定义函数将 Config 实例转换为字符串表示（带有一级缩进）
        fun toT1Str(
            tabNum: Int = 0,
            aStatesStringFun: ArrayList<String>.() -> String = {
                this.joinToString(separator = ",")
            },
            aCsStringFun: ArrayList<Constraint>.() -> String = {
                this.joinToString(",") {
                    it.toStr0()
                }
            },
        ): String {
            val stringBuilder = StringBuilder()
            val tsu = makeTabStringUnit(tabNum)

            stringBuilder.append("${tsu.tabNumStr0}(")
            stringBuilder.append("\n${tsu.tabNumStr1}(")
            stringBuilder.append(
                this.states.aStatesStringFun(),
            )
            stringBuilder.append("),\n${tsu.tabNumStr1}(")
            stringBuilder.append(
                this.varConstraintLHM.values.toList().toArrayList().aCsStringFun(),
            )
            stringBuilder.append("),\n${tsu.tabNumStr1}")
            stringBuilder.append(this.heap.getString())
            stringBuilder.append("\n)")

            return stringBuilder.toString().trim().removeEmptyLine()
        }

        // 使用自定义函数将 Config 实例转换为字符串表示（带有二级缩进）
        fun toT2Str(
            tabNum: Int = 0,
            aStatesStringFun: ArrayList<String>.() -> String = {
                this.joinToString(separator = ",")
            },
            aCsStringFun: ArrayList<Constraint>.() -> String = {
                this.joinToString(",") {
                    it.toStr0()
                }
            },
        ): String {
            val stringBuilder = StringBuilder()
            val tsu = makeTabStringUnit(tabNum)

            stringBuilder.append("${tsu.tabNumStr0}(")
            stringBuilder.append("\n${tsu.tabNumStr1}(")
            stringBuilder.append(
                this.states.aStatesStringFun(),
            )
            stringBuilder.append("),\n${tsu.tabNumStr1}(")
            stringBuilder.append(
                this.varConstraintLHM.values.toList().toArrayList().aCsStringFun(),
            )
            stringBuilder.append("),\n${tsu.tabNumStr1}")
            stringBuilder.append(this.heap.getString())
            stringBuilder.append("\n)")

            return stringBuilder.toString().trim().removeEmptyLine()
        }

        // 实现 IToStr 接口的 toStr 方法，提供 Config 实例的字符串表示
        override fun toStr(tabNum: Int): String {
            val sb = StringBuilder()
            val tsu = makeTabStringUnit(tabNum)
            sb.append(
                "${tsu.tabNumStr0}((${
                    this.states.joinToString(separator = ",")
                }),(\n"
            )
            this.varConstraintLHM.values.forEach {
                sb.append("${tsu.tabNumStr1}${it.toStr0()},\n")
            }
            sb.append("))")
            return sb.toString()
        }

        // 重写 toString 方法，调用 toT0Str 方法提供 Config 实例的字符串表示
        override fun toString(): String {
            return toT0Str()
        }

        // 根据函数查找符合条件的约束
        fun byFunFindC(function: (String) -> Boolean): Constraint? {
            return varConstraintLHM.values.firstOrNull {
                function(it.getVariablesString())
            }
        }

        // 根据名称查找约束
        fun byNameFindC(name: String): Constraint {
            return byFunFindC {
                name == it
            }!!
        }

        // 获取时间约束的时间值
        fun getTTime() = byNameFindC("t").getPointD()
    }

    // 定义一个开放类（可以被继承）PathLHM，泛型参数为 E 和 F
    abstract class PathLHM<E, F> : A2LHM<E, ArrayList<F>>() {
        abstract fun aFToEFun(f: F): E

        // privateTouch 方法，用于内部递归操作
        fun privateTouch(
            nowFs: ArrayList<F> = ArrayList(), // 当前路径上的 F 元素列表
            doFun: (ArrayList<F>) -> Unit = { _ -> },
        ) {
            doFun(nowFs)
            val nowF = nowFs.last() // 获取 nowFs 的最后一个元素
            val nowE = aFToEFun(nowF) // 使用扩展函数 toE 将 F 类型转换为 E 类型

            // 如果当前 E 已经存在于映射中
            if (this.containsKey(nowE)) {
                // 遍历与当前 E 关联的 F 列表
                this[nowE]!!.forEach {
                    nowFs.add(it) // 将 F 添加到 nowFs
                    // 递归调用
                    privateTouch(
                        nowFs,
                        doFun,
                    )
                    nowFs.removeLast() // 移除最后一个元素，回溯
                }
            }
        }

        // touch 方法，用于外部调用
        fun touch(
            firstF: F, // 起始的 F 元素
            initFun: (ArrayList<F>) -> Unit = { _ -> }, // 初始化函数
        ) {
            // 调用 privateTouch 进行递归操作
            privateTouch(
                arrayListOf(
                    firstF,
                ),
                initFun,
            )
        }
    }

    // 定义一个开放类（可以被继承）SimplePathLHM，泛型参数为 E
    open class SimplePathLHM<E> : PathLHM<E, E>() {
        // 伴生对象，用于创建 SimplePathLHM 实例
        companion object {
            fun <E> makeSimplePathLHM(): SimplePathLHM<E> {
                return SimplePathLHM()
            }
        }

        override fun aFToEFun(f: E) = f

        // touch 方法，用于外部调用
        fun touch(initFun: (ArrayList<E>) -> Unit = { _ -> }) {
            // 调用父类的 touch 方法进行递归操作
            touch(
                firstF = this.entries.first().key, // 使用第一个键作为起始点
                initFun = initFun,
            )
        }

        // plnToString 方法，用于将路径转换为字符串
        fun plnToString(
            aEToStringFun: (E) -> String = {
                it.toString() // 默认情况下，使用 toString 方法
            },
            sb: StringBuilder = StringBuilder(), // 字符串构建器，用于保存结果
        ): String {
            // 调用 touch 方法遍历所有路径
            touch { nowFs ->
                val nowF = nowFs.last() // 获取当前路径的最后一个元素
                val tabNum = nowFs.size - 1 // 计算缩进的数量
                // 向 StringBuilder 添加字符串
                sb.append(
                    "${
                        "\t".repeat(tabNum) // 添加缩进
                    }${
                        aEToStringFun(nowF) // 使用 aEToStringFun 函数将 E 类型转换为字符串
                    }\n"
                )
            }
            return sb.toString() // 返回最终的字符串
        }
    }

    class TimeEventChoiceConfigPathLHM : SimplePathLHM<TimedEventConfigTuple>() {
        companion object {
            fun makeTimeEventChoiceConfigPathLHM() = TimeEventChoiceConfigPathLHM()
        }
    }

    class TimeEventChoiceArrConfigPathLHM : SimplePathLHM<TimedEventListConfigTuple>() {
        companion object {
            fun makeTimeEventChoiceArrConfigPathLHM() = TimeEventChoiceArrConfigPathLHM()
        }
    }

    /**
     * F E F E F
     */
    // 定义一个开放类（可以被继承）WeightedPathLHM，泛型参数为 E 和 F
    open class WeightedPathLHM<E, F>(
        val aFWhenRoot: F, // 当元素作为根节点时的 F 值
    ) : PathLHM<E, A2Tuple<E, F>>(
    ) {
        override fun aFToEFun(f: A2Tuple<E, F>): E {
            return f.first
        }

        // touch 方法，用于外部调用
        fun touch(
            initFun: (ArrayList<A2Tuple<E, F>>) -> Unit = { _ -> }, // 初始化函数
        ) {
            // 调用父类的 touch 方法进行递归操作
            touch(
                firstF = getA2Tuple(
                    this.entries.first().key, // 使用第一个键作为起始点
                    aFWhenRoot // 使用 aFWhenRoot 作为起始点的 F 值
                ),
                initFun = initFun,
            )
        }

        // pln 方法，用于打印路径
        fun pln(
            aEToStringFun: (E) -> String = {
                it.toString() // 默认情况下，使用 toString 方法
            },
            aFToStringFun: (F) -> String = {
                it.toString() // 默认情况下，使用 toString 方法
            },
        ) {
            // 调用 touch 方法遍历所有路径
            touch { nowEFs ->
                val (nowE, nowF) = nowEFs.last() // 获取当前路径的最后一个元组
                // 打印路径
                println(
                    "${
                        "\t".repeat(nowEFs.size.modifiedMinus(1)) // 添加缩进
                    }${
                        aEToStringFun(nowE) // 使用 aEToStringFun 函数将 E 类型转换为字符串
                    },${
                        aFToStringFun(nowF) // 使用 aFToStringFun 函数将 F 类型转换为字符串
                    }"
                )
            }
        }
    }

    // 定义一个开放类（可以被继承）DoubleWeightedPathLHM，泛型参数为 E
    open class DoubleWeightedPathLHM<E>(
        aFWhenRoot: Double = 1.0, // 当元素作为根节点时的权重，默认为 1.0
        val aStringEToDoubleLHM: A2LHM<String, (E) -> Double> = A2LHM(), // 一个映射，用于计算特定字符串对应的权重函数
    ) : WeightedPathLHM<E, Double>(
        aFWhenRoot,
    ) {
        // 伴生对象，用于创建 DoubleWeightedPathLHM 实例
        companion object {
            fun <E> getDoublePathLHM(
                aFWhenRoot: Double = 1.0,
                aStringEToDoubleLHM: A2LHM<String, (E) -> Double> = A2LHM(),
            ): DoubleWeightedPathLHM<E> {
                return DoubleWeightedPathLHM(
                    aFWhenRoot,
                    aStringEToDoubleLHM,
                )
            }
        }

        fun touchWeight(
            doFun: (A2LHM<E, Double>, ArrayList<Pair<E, Double>>) -> Unit,
            debuggerList: DebuggerList = makeDebuggerList(),
        ) {
            val aListSumLHMM = A2LHM<E, Double>() // 用于存储每个节点的权重之和
            // 第一次遍历，计算每个节点的权重之和
            this.touch { nowEFs ->
                val nowE = nowEFs.last().first
                if (this.containsKey(nowE)) {
                    if (!aListSumLHMM.containsKey(nowE)) {
                        aListSumLHMM.add(nowE, this[nowE]!!.sumOf {
                            it.second
                        })
                    }
                }
            }

            // 第二次遍历，计算最终的权重
            this.touch { nowEFs: ArrayList<Pair<E, Double>> ->
                val nowE = nowEFs.last().first
                if (!this.containsKey(nowE)) {
                    doFun(aListSumLHMM, nowEFs)
                }
            }
        }

        // cal 方法，用于计算路径的权重
        fun cal(
            debuggerList: DebuggerList = makeDebuggerList(),
        ): A2LHM<String, Double> {
            val result = A2LHM<String, Double>() // 结果映射
            aStringEToDoubleLHM.forEach {
                result.add(it.key, 0.0) // 初始化结果映射
            }
            touchWeight(
                doFun = { aListSumLHMM, nowEFs ->
                    var thePathWeight = 1.0
                    nowEFs.withIndex().forEach { (index, value) ->
                        val (e, d) = value
                        thePathWeight *= d
                        if (index < nowEFs.size - 1) {
                            thePathWeight /= aListSumLHMM[e]!!
                        } else {
                            aStringEToDoubleLHM.forEach { (string, aFun) ->
                                result[string] = result[string]!! + thePathWeight * aFun(e)
                            }
                        }
                    }
                },
                debuggerList = debuggerList,
            )
            return result
        }

        fun getCalString(debuggerList: DebuggerList = makeDebuggerList()): A2LHM<String, String> {
            val result = A2LHM<String, StringBuilder>() // 结果映射
            aStringEToDoubleLHM.map {
                result.add(it.key, StringBuilder("0")) // 初始化结果映射
            }
            touchWeight(
                doFun = { aListSumLHMM, nowEFs ->
                    val sb2 = StringBuilder("1")
                    nowEFs.withIndex().forEach { (index, value) ->
                        val (e, d) = value
                        sb2.append(
                            d.toPreSymbolString(
                                aMultipleSign
                            )
                        )
                        if (index < nowEFs.size - 1) {
                            sb2.append(
                                aListSumLHMM[e]!!.toPreSymbolString(
                                    aDivisionSign
                                )
                            )
                        } else {
                            aStringEToDoubleLHM.forEach { (string, aFun) ->
                                result[string]!!.append("\n\t+((${sb2})*${aFun(e)})")
                            }
                        }
                    }
                },
                debuggerList = debuggerList,
            )
            val result2 = A2LHM<String, String>() // 结果映射
            result.forEach { (t, u) ->
                result2.add(t, u.toString())
            }
            return result2
        }
    }

    //路径簇
    class TimeEventChoiceConfigWeightedPathLHM(
        aFWhenRoot: Double = 1.0,
        aStringEToDoubleLHM: A2LHM<String, (TimedEventConfigTuple) -> Double> = A2LHM(),
    ) : DoubleWeightedPathLHM<TimedEventConfigTuple>(
        aFWhenRoot,
        aStringEToDoubleLHM,
    ) {
        companion object {
            fun makeTimeEventChoiceConfigWeightedPathLHM(
                aFWhenRoot: Double = 1.0,
                aStringEToDoubleLHM: A2LHM<String, (A2Tuple<TimedEvent?, Config>) -> Double> = A2LHM(),
            ) = TimeEventChoiceConfigWeightedPathLHM(aFWhenRoot, aStringEToDoubleLHM)
        }
    }

    //路径簇
    class TimeEventChoiceArrConfigWeightedPathLHM(
        aFWhenRoot: Double = 1.0,
        aStringEToDoubleLHM: A2LHM<String, (TimedEventListConfigTuple) -> Double> = A2LHM(),
    ) : DoubleWeightedPathLHM<TimedEventListConfigTuple>(
        aFWhenRoot,
        aStringEToDoubleLHM,
    ) {
        companion object {
            fun makeTimeEventChoiceArrConfigWeightedPathLHM(
                aFWhenRoot: Double = 1.0,
                aStringEToDoubleLHM: A2LHM<String, (TimedEventListConfigTuple) -> Double> = A2LHM(),
            ) = TimeEventChoiceArrConfigWeightedPathLHM(aFWhenRoot, aStringEToDoubleLHM)
        }
    }
}